/*
 * @Descripttion: 
 * @version: 
 * @Author: turingchang
 * @Date: 2020-12-22 17:11:58
 * @LastEditors: turingchang
 * @LastEditTime: 2020-12-25 17:50:17
 */

'use strict';

const Service = require('egg').Service;
const schedule = require('node-schedule');
const { Op } = require("sequelize");

class JobService extends Service {

    /**
     * 查询计划任务
     * @param {*}} params :{id:id,group_id:group_id,name:name}
     */
    async get(params){
        const ctx =this.ctx;
        await ctx.service.job.jobGroup.initDefaultGroup();
        let res = {
            success:false,
            msg:'',
            response_code_enum:null,
        }      
        let jobs ={};
        let where ={};  
        if(!params){
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return jobs;
        }
        if(params.id && ctx.app.utils.validator.isNum(params.id)){
            where.id =params.id;
        }
        if(params.group_id && ctx.app.utils.validator.isNum(params.group_id)){
            where.group_id =params.group_id;
        }
        if(params.name){
            where.name ={
                [Op.like]: `%${params.name}%`
            }
        }
        if(params.uuid){
            // where.uuid ={
            //     [Op.like]: `%${params.uuid}%`
            // }
            where.uuid =params.uuid;
        }
        if(params.uuids){
            where.uuids =params.uuids;
        }
        where.delete_time =null;
        jobs = await this.ctx.model.Job.Job.findAll({
            attributes: { exclude: ['delete_time']},
            where:where,
        });
        return jobs;
    }
    
    /**
     * 添加计划任务.post
     */
    async add(params){
        const ctx =this.ctx;
        let res = {
            success:false,
            msg:'',
            response_code_enum:null,
        }        
        let job = {};
        let jobs = {};
        let job_group ={};

        if(!params){
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return res;
        }
        if(!params.name || !params.cron || !params.type || !params.value){
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return res;
        }
        params.uuid = ctx.app.utils.generator.uid();
        //验证cron表达式
        let cron_validate = ctx.app.utils.cron.validate(params.cron);
        if( cron_validate !== true){
            res.msg = cron_validate;
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return res;
        }
        //验证分组
        if(params.group_id && ctx.app.utils.validator.isNum(params.group_id)){
            job_group = await ctx.service.job.jobGroup.get({
                id:params.group_id
            });
            if(job_group && job_group.id>0){
                params.group_id = job_group.id;
            }else{
                params.group_id = 0;
            }
        }
        params.group_id = params.group_id || 0 ;
        if( params.group_id  == 0 ){
            job_group = await ctx.service.job.jobGroup.get({
                pid:0,
                name:'default',
            });
            if(job_group && job_group.id>0){
                params.group_id = job_group.id;
            }else{
                await ctx.service.job.jobGroup.initDefaultGroup();
                job_group = await ctx.service.job.jobGroup.get({
                    pid:0,
                    name:'default',
                });
                if(job_group && job_group.id>0){
                    params.group_id = job_group.id;
                }
            }
        }

        if(params.name){
            jobs = await this.get({
                name:params.name,
                group_id:params.group_id,
            });
            if(!ctx.app.utils.validator.isNull(jobs)){
                res.response_code_enum = ctx.app.enums.response_code_enum.param_error;
                res.msg = "该名称已存在";
                return res;
            }
            job = jobs[0];
        }
        params.uuid = ctx.app.utils.generator.uid();
        let save_result = await ctx.app.model.Job.Job.create(params);
        if(!save_result){
            res.msg = "添加失败";
            res.response_code_enum = ctx.app.enums.response_code_enum.server_error;
            return res;
        }
        if(!save_result.id || save_result.id<=0){
            res.msg = "添加失败";
            res.response_code_enum = ctx.app.enums.response_code_enum.server_error;
            return res;
        }
        
        let schedule_job =await schedule.scheduleJob(params.uuid,params.cron, async ()=>{
            let time = schedule_job.nextInvocation();
            let convertedTime;
            if(time){
                convertedTime = new Date(time);
                convertedTime = ctx.app.utils.time.moment(convertedTime).format("YYYY-MM-DD HH:mm:ss");
            }
            console.log(100,convertedTime);
            let request_result =await this.request({
                url:save_result.value,
                method:save_result.request_type,
                data:save_result.request_params || {},
            });
            request_result.job_id = save_result.id;
            request_result.next_invocation = convertedTime;
            await ctx.service.job.jobLog.add(request_result);
        }); 
        if(schedule_job){
            save_result.status = ctx.app.enums.job_status.normal.value;
            await save_result.save();
            res.success =true;
            res.response_code_enum = ctx.app.enums.response_code_enum.success;
            return res;
        }
        return res;
    }
        
    /**
     * 取消计划任务
     */
    async cancel(params){
        const ctx =this.ctx;
        let res = {
            success:false,
            msg:'',
            response_code_enum:null,
        }  
        let jobs ={};
        let job ={};
        let job_log ={};
        let where ={};
        if(!params){
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return res;
        }
        if(!params.uuid && !params.uuids){
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return res;
        }        
        if(params.uuid){
            where.uuid =params.uuid;
        }
        if(params.uuids){
            where.uuid =params.uuids;
        }        
        jobs = await this.get({
            uuid:params.uuid || null,
            uuids:params.uuids || null,
        });
        // console.log(1111,jobs);
        if(ctx.app.utils.validator.isNull(jobs)){
            res.msg ='计划任务不存在';
            res.response_code_enum = ctx.app.enums.response_code_enum.param_error;
            return res;
        }
        for(let item of jobs){
            let cancel_result = schedule.cancelJob(item.uuid);
            if(cancel_result === true){
                item.status = ctx.app.enums.job_status.cancelled.value;
                await item.save();       
                job_log.job_id = item.id;
                job_log.response_result ='计划任务已取消';             
                job_log.tag = job_log.response_result;
                job_log.response_success =1;
                await ctx.service.job.jobLog.add(job_log);         
            }
        }
        res.success =true;
        return res;
    }
    
    /**
     * 发起请求.返回结果状态码
     */
    async request(params){
        const ctx =this.ctx;
        let request_result = {
            status:0,
            response_result:'',
            response_success:0,
        };
        if(!params){
            request_result.response_result ="无效的内容";
            return request_result;
        }
        if(!params.url || !params.method){
            request_result.response_result ="无效的链接或请求方式";
            return request_result;
        }
        if(!ctx.app.utils.validator.checkUrl(params.url)){
            request_result.response_result ="无法识别的链接";
            return request_result;
        }
        request_result = await ctx.app.curl(params.url,{
            method:params.method,
            data:params.data || {},
            timeout: 5000,
        })
        // console.log('执行请求结果：',request_result);
        let status = request_result.status;        
        switch(status){
            case 200:request_result.response_result=`执行成功,响应码${status}`;request_result.response_success =1;break;
            default:request_result.response_result=`执行成功但响应失败,响应码${status}`;request_result.response_success =0;break;
        }        
        return request_result;
    }

    async getSystemSchedule(){
        for (let i in schedule.scheduledJobs) {
            console.log("已开启的定时任务名称："+i);
        }
    }

    /**
     * 编辑计划任务
     * @param {*} params 
     */
    async edit(params){
        const ctx =this.ctx;
        let job ={};
        let job_old_status ='';
        let res = {
            success:false,
            msg:'',
            response_code_enum:null,
        }  
        if(!params){
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return res;
        }
        if(!params.id){
            res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
            return res;
        }

        job = await this.ctx.model.Job.Job.findOne({
            attributes: { exclude: ['delete_time']},
            where:{
                id:params.id
            },
        });
        if(!job){
            res.msg ="该计划任务不存在";
            res.response_code_enum = ctx.app.enums.response_code_enum.param_error;
            return res;
        }
        job_old_status = job.status;
        if(params.status){
            switch(params.status){
                case ctx.app.enums.job_status.normal.value:
                case ctx.app.enums.job_status.manual_stop.value:
                case ctx.app.enums.job_status.stoped.value:
                case ctx.app.enums.job_status.cancelled.value:
                case ctx.app.enums.job_status.deleted.value:break;
                default:
                    res.msg ="无效的计划任务状态";
                    res.response_code_enum = ctx.app.enums.response_code_enum.param_error;
                    return res;
            }
            job.status = params.status;
        }
        if(params.next_invocation){
            try{
                params.next_invocation = ctx.app.utils.time.moment(params.next_invocation).format("YYYY-MM-DD HH:mm:ss");
                job.next_invocation = params.next_invocation ;
            }
            catch(e){

            }            
        }
        try {
            await job.save();            
        } catch (e) {
            res.response_code_enum = ctx.app.enums.response_code_enum.server_error;
            res.msg = "编辑失败";
            return res;
        }   
        let job_log ={};
        if(params.status){
            switch(params.status){
                case ctx.app.enums.job_status.normal.value:
                    await this.start({
                        uuid:job.uuid
                    });                            
                    break; 
                case ctx.app.enums.job_status.stoped.value:
                    await this.stop({
                        uuid:job.uuid,
                        stop_type:ctx.app.enums.job_stop_type.auto.value
                    });                            
                    break; 
                case ctx.app.enums.job_status.manual_stop.value:
                    await this.stop({
                        uuid:job.uuid,
                        stop_type:ctx.app.enums.job_stop_type.manual.value
                    });                            
                    break; 
                case ctx.app.enums.job_status.cancelled.value:
                    await this.cancel({
                        uuid:job.uuid
                    });    
                    break; 
                case ctx.app.enums.job_status.deleted.value:
                    await this.cancel({
                        uuid:job.uuid
                    });    
                    job_log.job_id = job.id;
                    job_log.response_result ='计划任务已删除';             
                    job_log.tag = job_log.response_result;
                    job_log.response_success =1;
                    await ctx.service.job.jobLog.add(job_log);
                    break; 
            }
        }       
        
        res.response_code_enum = ctx.app.enums.response_code_enum.success;
        res.msg = "编辑成功";     
        res.success =true;
        return res;
    }

    /**
     * 启动计划任务
     */
    async start(params){
        const ctx =this.ctx;
        let jobs ={};
        let res = {
            success:false,
            msg:'',
            response_code_enum:null,
        }  
        let where ={};
        // if(!params){
        //     res.response_code_enum = ctx.app.enums.response_code_enum.miss_required_params;
        //     return res;
        // }
        if(params && params.uuid){
            where.uuid =params.uuid;
        }
        if(params && params.uuids){
            where.uuid =params.uuids;
        }
        where.delete_time =null;
        where.status ={
            [Op.in]:[ctx.app.enums.job_status.init.value,ctx.app.enums.job_status.normal.value,ctx.app.enums.job_status.stoped.value,]
        };
        jobs = await this.ctx.model.Job.Job.findAll({
            attributes: { exclude: ['delete_time']},
            where:where
        });      
        // console.log(200,jobs);  
        if(ctx.app.utils.validator.isNull(jobs)){
            res.msg ="该计划任务不存在";
            res.response_code_enum = ctx.app.enums.response_code_enum.param_error;
            return res;
        }
        // return jobs;        
        let job ={};
        let start_jobs =[];
        let un_start_jobs =[];
        let job_save_res =false;
        let job_log={};

        //查找有效的正在运行的计划任务
        for (let i in schedule.scheduledJobs){
            job ={};
            job = jobs.filter(item=>item.uuid == i);
            if(job){
                job = job[0];
                start_jobs.push(job);
            }
        } 
        //筛选未运行的计划任务       
        un_start_jobs = ctx.app.utils.array.getArrDifference(jobs,start_jobs);
        if(un_start_jobs){
            for (let item of un_start_jobs) {
                if(item){
                    let schedule_job = await schedule.scheduleJob(item.uuid,item.cron, async ()=>{
                        let time = schedule_job.nextInvocation();
                        let convertedTime;
                        if(time){
                            convertedTime = new Date(time);
                            convertedTime = ctx.app.utils.time.moment(convertedTime).format("YYYY-MM-DD HH:mm:ss");
                        }
                        let request_result =await this.request({
                            url:item.value,
                            method:item.request_type,
                            data:item.request_params || {},
                        });
                        request_result.job_id = item.id;
                        request_result.next_invocation = convertedTime;
                        await ctx.service.job.jobLog.add(request_result);
                        console.log(202,`服务${item.name}(${item.uuid})启动成功`);
                    }); 
                    if(!ctx.app.utils.validator.isNull(schedule_job)){
                        item.status = ctx.app.enums.job_status.normal.value;
                        job_save_res = await item.save();
                        if(!ctx.app.utils.validator.isNull(job_save_res)){
                            job_log.job_id = item.id;
                            job_log.response_result ='计划任务启动';
                            job_log.tag = job_log.response_result;
                            job_log.response_success =1;
                            await ctx.service.job.jobLog.add(job_log);
                        }
                    }
                }
            }
        }
        
        res.response_code_enum = ctx.app.enums.response_code_enum.success;
        res.msg = "启动成功";     
        res.success =true;
        return res;
    }

    /**
     * 停止计划任务
     * @param {*} params 
     */
    async stop(params){
        const ctx =this.ctx;
        let jobs ={};
        let res = {
            success:false,
            msg:'',
            response_code_enum:null,
        }  
        let where ={};        
        if(params && params.uuid){
            where.uuid =params.uuid;
        }
        if(params && params.uuids){
            where.uuid =params.uuids;
        }
        where.delete_time =null;
        where.status ={
            [Op.in]:[ctx.app.enums.job_status.init.value,ctx.app.enums.job_status.normal.value,]
        };
        jobs = await this.ctx.model.Job.Job.findAll({
            attributes: { exclude: ['delete_time']},
            where:where
        });      
        // console.log(200,jobs);  
        if(ctx.app.utils.validator.isNull(jobs)){
            res.msg ="该计划任务不存在或无法停止";
            res.response_code_enum = ctx.app.enums.response_code_enum.param_error;
            return res;
        }
        if(params.stop_type){
            switch(params.stop_type){
                case ctx.app.enums.job_stop_type.auto.value:params.stop_type = ctx.app.enums.job_stop_type.auto.value;break;
                default:params.stop_type = ctx.app.enums.job_stop_type.manual.value;break;
            }            
        }else{
            params.stop_type = ctx.app.enums.job_stop_type.manual.value;
        }
        // return jobs;        
        let job_save_res =false;
        let job_log={};
        for(let job of jobs){
            switch(params.stop_type){
                case ctx.app.enums.job_stop_type.auto.value: job.status = ctx.app.enums.stoped.value;break;
                default:job.status = ctx.app.enums.job_stop_type.manual_stop.value;
            }  
            job_save_res = await job.save();
            if(job_save_res == true){
                job_log.job_id = job.id;
                switch(params.stop_type){
                    case ctx.app.enums.job_stop_type.auto.value: job_log.response_result ='计划任务自动暂停';break;
                    default:job_log.response_result ='计划任务手动暂停';
                }                 
                job_log.tag = job_log.response_result;
                job_log.response_success =1;
                await ctx.service.job.jobLog.add(job_log);
            }
        }
        
        res.response_code_enum = ctx.app.enums.response_code_enum.success;
        res.msg = "暂停成功";     
        res.success =true;
        return res;
    }
  
}

module.exports = JobService;